package org.example.netty.test.nio.netty.chat.protocol;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import lombok.extern.slf4j.Slf4j;
import org.example.netty.test.nio.netty.chat.bean.*;
import org.example.netty.test.nio.netty.chat.common.MagicNumber;
import org.example.netty.test.nio.netty.chat.common.MessageType;
import org.example.netty.test.nio.netty.chat.common.SerializationVersion;
import org.example.netty.test.nio.netty.chat.util.JSONSerializeUtils;
import org.example.netty.test.nio.netty.chat.util.JDKSerializeUtils;

import java.util.List;

/**
 * @author zp
 * @version 1.0
 * @description: 自定义的消息解码器
 * +---------------------------------------------------------------+
 * | 魔数 4byte | 协议版本号 4byte | 序列化算法 4byte | 报文类型 4byte  |
 * +---------------------------------------------------------------+
 * | 状态 4byte | 保留字段 4byte |数据长度 4byte|数据内容 （长度不定）    |
 * +---------------------------------------------------------------+
 * @date 2023/3/30 19:40
 */
@Slf4j
public class MyAgreeOnMessageCodec extends ByteToMessageCodec<AbsMessageBase> {
    /**
     * @param ctx
     * @param msg
     * @param out
     * @description: 编码 把一个对象编码成一个byte数组
     * @author zp
     * @date: 2023/3/30 20:02
     */
    @Override
    public void encode(ChannelHandlerContext ctx, AbsMessageBase msg, ByteBuf out) throws Exception {
        writeMessageBase(msg, out);
        byte[] data = null;
        data = serialize(msg, data);
        out.writeInt(data.length);
        // 写入消息
        out.writeBytes(data);
    }

    /**
     * @param msg
     * @param out
     * @description: 写入消息头 都是一致的
     * @author zp
     * @date: 2023/4/2 17:36
     */
    private static void writeMessageBase(AbsMessageBase msg, ByteBuf out) {
        // 写入两个字节的魔数
        out.writeInt(msg.getMagicNumber());
        // 协议版本号 一个字节
        out.writeInt(msg.getVersion());
        // 序列化协议 一个字节
        out.writeInt(msg.getSerializationAlgorithm());
        // 消息类型 一个字节
        out.writeInt(msg.getMessageType());
        // 消息状态 一个字节
        out.writeInt(msg.messageTypeStatus());
        // 保留字段
        out.writeInt(99);
    }

    /**
     * @param ctx
     * @param in
     * @param out
     * @description: 解码的方法
     * @author zp
     * @date: 2023/4/2 17:33
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 获取魔数
        int magicNumber = in.readInt();
        // 校验是否是指定协议的魔数 如果不是就抛出异常 协议不适用
        isMagicNumber(magicNumber);
        // 获取版本号
        int version = in.readInt();
        // 获取序列化类型
        int serializationAlgorithm = in.readInt();
        // 获取消息类型
        int messageType = in.readInt();
        // 获取状态
        int status = in.readInt();
        // 保留字段
        int continueInt = in.readInt();
        // 获取消息长度
        int length = in.readInt();
        byte[] messageBytes = new byte[length];
        in.readBytes(messageBytes);
        out.add(deserializeMessage(serializationAlgorithm, messageBytes,messageType));
        //log.info("收到消息:{}", message);
        // log.info("{},{},{},{},{},{},{}", magicNumber, version, serializationAlgorithm, messageType, status, continueInt, length);
    }

    /**
     * @param msg
     * @param data
     * @return byte[]
     * @description: 序列化编码的方法
     * @author zp
     * @date: 2023/4/2 17:33
     */
    private static byte[] serialize(AbsMessageBase msg, byte[] data) {
        // 获取使用JDK 序列化之后字节
        if (SerializationVersion.JDK_SERIALIZE == msg.getSerializationAlgorithm()) {
            JDKSerializeUtils jdkSerializeUtils = new JDKSerializeUtils();
            data = jdkSerializeUtils.serialize(msg);
        } else if (SerializationVersion.JSON_SERIALIZE == msg.getSerializationAlgorithm()) {
            JSONSerializeUtils jsonSerializeUtils = new JSONSerializeUtils();
            data = jsonSerializeUtils.serialize(msg);
        }
        // 写入四个字节的消息长度
        assert data != null;
        return data;
    }

    /**
     * @param serializationAlgorithm
     * @param messageBytes
     * @return T
     * @description: 序列化的解码方法
     * @author zp
     * @date: 2023/4/2 17:34
     */
    private static <T> T deserializeMessage(int serializationAlgorithm, byte[] messageBytes,int messageType) throws Exception {
        if (SerializationVersion.JDK_SERIALIZE == serializationAlgorithm) {
            JDKSerializeUtils jdkSerialization = new JDKSerializeUtils();
            return (T) jdkSerialization.deserialize(messageBytes, MessageType.messageTypeMap.get(messageType));

        } else if (SerializationVersion.JSON_SERIALIZE == serializationAlgorithm) {
            JSONSerializeUtils jsonSerializeUtils = new JSONSerializeUtils();
            return (T) jsonSerializeUtils.deserialize(messageBytes, MessageType.messageTypeMap.get(messageType));
        }
        return null;
    }

    /**
     * @param magicNumber
     * @description: 判断魔数 确定协议是否一致
     * @author zp
     * @date: 2023/4/2 17:34
     */
    private static void isMagicNumber(int magicNumber) {
        if (magicNumber != AbsMessageBase.magicNumber) {
            throw new RuntimeException("magic number is not right");
        }
    }
}
